#include "DBChecker.h"
#include "FLock.h"
#include "logger.h"

#include <map>
#include <string>

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <cerrno>
#include <cstdlib>

using namespace CExpert;

namespace{
	typedef std::map<std::string, DBChecker*> CheckerMap;
	CheckerMap map;
}

bool cexpert_get_is_available_ex(const char* conn_str_c, time_t* check_time){
	//XXX init log shouldn't be here XXX
	//init_log("cexpert_c_client");
	//log("starting get_is_available for %s", conn_str_c);
	//TODO thread-safety
	std::string conn_str(conn_str_c);
	CheckerMap::const_iterator it = map.find(conn_str);
	//XXX possible leaks
	if (it == map.end()){
		//log("creating get_is_available for %s", conn_str_c);
		try{
			DBChecker* checker = new DBChecker(conn_str_c, false);
			//log("checker created get_is_available for %s", conn_str_c);
			map.insert(std::make_pair(conn_str, checker));
			//log("checker inserted get_is_available for %s", conn_str_c);
			DBChecker::status status = checker->get_status();
			*check_time = status.check_time;
			return status.is_ok;
		}
		catch (std::runtime_error& ex){
			log("FAIL get_is_available for %s: %s", conn_str_c, ex.what()
);
			return true; // если ничего не можем сказать по существу, отвечаем ОК
		}
	} else{
	//log("reusing get_is_available for %s", conn_str_c);
		DBChecker::status status = it->second->get_status();
		*check_time = status.check_time;
		return status.is_ok;
	}
}

bool cexpert_get_is_available(const char* conn_str_c){
	time_t aux;
	return cexpert_get_is_available_ex(conn_str_c, &aux);
}

bool cexpert_is_registered(const char* conn_str){
	return FLock::held_by(conn_str) >= 0;
}

bool cexpert_do_register(const char* conn_str){
	pid_t fork_result = fork();
	if (fork_result < 0){
		log("fork fails: %d", errno);
		return false;
	}
	if (fork_result == 0){
		//we're child, now - run daemon process
		execlp("cexpert_s", "cexpert_s", conn_str, (char*)0);
		log("can't exec: %d", errno);
		exit(-1);
	}
	//parent process should wait child: it'll daemonize immediately
	if (waitpid(fork_result, NULL, 0) < 0)
		return false;
	return true;
}
bool cexpert_do_unregister(const char* conn_str){
	return FLock::kill_holder(conn_str);
}
